﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarbonExchange.AmeeQuery;
using CarbonExchange.Dal;

namespace CarbonExchange.Bll
{
    public class SiteFactory : FactoryBase<Site>
    {
        public SiteFactory() : base() { }
        public SiteFactory(IAmeeCommand command) : base(command) { }
        public SiteFactory(Context context) : base(context) { }
        public SiteFactory(IAmeeCommand command, Context context) : base(command, context) { }

        internal class SelectionJoin
        {
            internal Dal.Site Site{get; set;}
            internal Dal.SiteRoot SiteRoot { get; set; }
        }

        private IQueryable<SiteFactory.SelectionJoin> GetSelection()
        {
            var tmp = from site in DalContext.Sites
                      join siteRoot in DalContext.SiteRoots on site.Id equals siteRoot.SiteId
                      where !site.Deleted && !siteRoot.Deleted && siteRoot.Default
                      orderby site.Name
                      select new SelectionJoin() { Site = site, SiteRoot = siteRoot };
            return tmp;
        }

        public IQueryable<Site> GetAll()
        {
            return GetSelection().Where(x=>x.SiteRoot.Default).Convert(DalContext);
        }

        public Site GetByPath(string path)
        {
            var tmp = (from item in DalContext.SiteRoots where item.Url == path select item).FirstOrDefault();
            if (tmp != null)
            {
                return GetSelection().Where(x => x.Site.Id == tmp.SiteId).SingleOrDefault().Convert(DalContext);
            }
            else
            {
                return null;
            }
        }

        public Site GetById(int id)
        {
            return GetSelection().Where(x => x.Site.Id == id).SingleOrDefault().Convert(base.DalContext);
        }

        protected override object GetDataItemForUpdate(Site item)
        {
            Dal.Site dalSite = null;
            //find or create new site db obj and set created to utc if creating new
            if (item.Id > 0)
            {
                dalSite = (from site in base.DalContext.Sites where site.Id == item.Id select site).Single();
            }
            else
            {
                dalSite = new CarbonExchange.Dal.Site();
                dalSite.Created = DateTime.UtcNow;

                DalContext.Sites.InsertOnSubmit(dalSite);
            }

            dalSite.Name = item.Name;

            Dal.SiteRoot dalSiteRoot = (from siteRoot in dalSite.SiteRoots where siteRoot.Default == true select siteRoot).SingleOrDefault();
            if (dalSiteRoot == null)
            {
                dalSiteRoot = new Dal.SiteRoot() { Default = true };
                dalSite.SiteRoots.Add(dalSiteRoot);
            }
            dalSiteRoot.Url = item.RootUrl;

            return dalSite;
        }

        protected override void PopulateDataChanges(Site item, object dataItem)
        {
            var tmp = dataItem as Dal.Site;
            item.Id = tmp.Id;
            item.Created = tmp.Created.ToLocalTime();
        }

        protected override void MarkItemForDeletion(Site item)
        {
            Dal.Site dalSite = (from site in base.DalContext.Sites where site.Id == item.Id select site).Single();
            if (dalSite != null)
            {
                dalSite.Deleted = true;
                foreach (Dal.SiteRoot root in dalSite.SiteRoots)
                {
                    root.Deleted = true;
                }
            }
        }

        protected override void Validate(IQueryable<Site> items, CarbonExchange.Bll.Exceptions.ValidationException exp)
        {
            items.Validate(exp, base.DalContext);
        }
    }

    internal static class SiteFactoryConverter
    {
        internal static IQueryable<Site> Convert(this IQueryable<SiteFactory.SelectionJoin> source, Dal.Context context)
        {
            return source.Select(x => ConvertItem(x, context));
        }

        internal static Site Convert(this SiteFactory.SelectionJoin source, Dal.Context context)
        {
            return ConvertItem(source, context);
        }

        internal static Site ConvertItem(SiteFactory.SelectionJoin item, Dal.Context context)
        {
            if (item != null)
            {
                return new Site()
                {
                    Id = item.Site.Id,
                    Name = item.Site.Name,
                    RootUrl = item.SiteRoot.Url,
                    Created = DateTime.SpecifyKind(item.Site.Created, DateTimeKind.Utc).ToLocalTime(),
                    countryFactory = new CountryFactory(context)
                };
            }
            else
            {
                return null;
            }
        }

        internal static void Validate(this IQueryable<Site> sites, Exceptions.ValidationException e, Dal.Context context)
        {
            foreach(Site site in sites)
            {
                site.Validate(e, context);
            }
            //check same RootUrl not uploaded twice
            var duplicates = sites.GroupBy(x=>x.RootUrl).Where(x=> x.Count() > 1);
            foreach(var duplicate in duplicates)
            {
                e.Add(string.Format("Root Url '{0}' specified more than one in update", duplicate.Key));
            }
        }

        internal static void Validate(this Site site, Exceptions.ValidationException e, Dal.Context context)
        {
            if (string.IsNullOrEmpty(site.Name))
            {
                e.Add("Site Name must be specified");
            }
            //check root url unique
            if (context.SiteRoots.Where(x => x.Url == site.RootUrl && x.SiteId != site.Id).Count() > 0)
            {
                e.Add(string.Format("Root Url '{0}' is already used by another site", site.RootUrl));
            }
            if (context.SiteRoots.Where(x => x.Url == site.RootUrl && x.SiteId == site.Id && !x.Default).Count() > 0)
            {
                e.Add(string.Format("Root Url '{0}' is already used by this site as an alternative site mapping.", site.RootUrl));
            }
        }
    }

}
